/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
//ResultSetGenericModelBinder binder = new ResultSetGenericModelBinder();
//ArrayList<ImportCar> items = binder.BindModels(results);
package DBLayer;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Moulde
 */
public class GenericModelBinder<T>
{
    Class<T> type;

    public GenericModelBinder(Class<T> type)
    {
        this.type = type;
    }

    public T BindModel(ResultSet result) throws
            SQLException, InstantiationException, IllegalAccessException
    {
        if(result.next())
        {
            return BindSingleModel(result);
        }
        return null;
    }

    public ArrayList<T> BindModels(ResultSet result) throws
            SQLException, InstantiationException, IllegalAccessException
    {
        ArrayList<T> objects = new ArrayList<T>();

        while (result.next())
        {
            objects.add(BindSingleModel(result));
        }

        return objects;
    }
    String[] databaseColumns;
    Field[] classFields;
    Field[] superClassFields;

    private T BindSingleModel(ResultSet result) throws
            SQLException, InstantiationException, IllegalAccessException
    {
        if (databaseColumns == null)
        {
            databaseColumns = GetColumnNames(result);
        }

        if (classFields == null)
        {
            Field[] subClassFields = type.getDeclaredFields();
            superClassFields = new Field[0];
            if (type.getSuperclass() != null)
            {
                superClassFields = type.getSuperclass().getDeclaredFields();
            }
            classFields = subClassFields;
        }

        //Opretter ny instans af type
        T instanceObject = type.newInstance();

        //Løber gennem kolonnenavne i tabellen
        for (String columnName : databaseColumns)
        {
            String dbColName = columnName;
            String fieldName = "";
            //Try catch fordi reflection risikerer at smide exceptions
            try
            {
                boolean fieldIsFound = false;
                //Løber felter på klassen igennem og sammenligner kolonnenavn
                //med feltnavn, eller med MapName annotationen's columnname
                for (Field field : classFields)
                {
                    fieldName = "";
                    //Hvis der ligger en annotation på det valgte field, bruges dens værdi istedet for felt navnet
                    if (field.isAnnotationPresent(MapName.class))
                    {
                        fieldName = field.getAnnotation(MapName.class).ColumnName();
                        //Hvis værdierne passer sammen så er et match fundet og den hopper ud af for loopet.
                        if (fieldName.toLowerCase().equals(dbColName.toLowerCase()) && fieldName.length() > 0)
                        {
                            fieldName = field.getName();
                            fieldIsFound = true;
                            break;
                        }
                    }
                    //Hvis der ikke eksisterer en MapName annotation sammenlignes kolonne navnet med felt navnet
                    else if (field.getName().equals(dbColName))
                    {
                        fieldIsFound = true;
                        break;
                    }
                }

                if (fieldIsFound)
                {
                    Field field;
                    if (fieldName.length() > 0)
                    {
                        field = instanceObject.getClass().getDeclaredField(fieldName);
                    }
                    else
                    {
                        field = instanceObject.getClass().getDeclaredField(columnName);
                    }

                    //overrider access modifyerene så den kan tilgå private variabler udenfor klassen
                    field.setAccessible(true);
                    Type currentType = field.getType();

                    //finder typen på feltet og henter data ud fra resultsettet med den rette metode
                    //Her bruges getInt metoden
                    if (currentType == Integer.class || currentType == int.class)
                    {
                        int value = result.getInt(dbColName);
                        field.setInt(instanceObject, value);
                    }
                    //Her bruges getString metoden
                    else if (currentType == String.class)
                    {
                        String value = result.getString(dbColName);
                        field.set(instanceObject, value);
                    }
                    //Her bruges getBigDecimal
                    else if (currentType == double.class || currentType == Double.class)
                    {
                        double value = result.getBigDecimal(dbColName).doubleValue();
                        field.set(instanceObject, value);
                    }
                    else
                    {
                        field.set(instanceObject, result.getObject(dbColName));
                    }
                }
            }
            catch (IllegalAccessException ex)
            {
                Logger.getLogger(GenericModelBinder.class.getName()).log(Level.SEVERE, null, ex);
            }
            catch (IllegalArgumentException ex)
            {
                Logger.getLogger(GenericModelBinder.class.getName()).log(Level.SEVERE, null, ex);
            }
            catch (NoSuchFieldException ex)
            {
                Logger.getLogger(GenericModelBinder.class.getName()).log(Level.SEVERE, null, ex);
            }
            catch (SecurityException ex)
            {
                Logger.getLogger(GenericModelBinder.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        //Returnerer instansen af objektet
        return instanceObject;
    }

    public String[] GetColumnNames(ResultSet results) throws SQLException
    {
        ResultSetMetaData metaData = results.getMetaData();
        int columnCount = metaData.getColumnCount();
        String[] columns = new String[columnCount];

        for (int i = 0; i < columnCount; i++)
        {
            columns[i] = metaData.getColumnName(i + 1);
        }

        return columns;
    }
}
